﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Forms;
using System.IO;
using Microsoft.Win32;
using System.Runtime.Serialization.Formatters.Binary;

namespace Light_Notes
{
    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private NotifyIcon notifyIcon;
        private HotkeyEx hotkey;
        private FileInfo fi;
        private bool isChanged;
        private FileSystemWatcher watcher;

        public MainWindow()
        {
            InitializeComponent();
            isChanged = false;

            Left = Properties.Settings.Default.left;
            Top = Properties.Settings.Default.top;
            Width = Properties.Settings.Default.width;
            Height = Properties.Settings.Default.height;
            WindowState = Properties.Settings.Default.state;

            string folder = Properties.Settings.Default.folder;
            if (folder.Length == 0)
                folder = Environment.GetFolderPath(Environment.SpecialFolder.Personal) + @"\Light Notes";
            Properties.Settings.Default.folder = folder;
            Properties.Settings.Default.Save();

            DirectoryInfo di = new DirectoryInfo(folder);
            di.Create();
            fi = new FileInfo(di.FullName + @"\Notes.ln");
            if (fi.Exists)
            {
                fi.CopyTo(fi.FullName + ".bak", true);
                Load();
            }
            else NewTab();

            watcher = new FileSystemWatcher();
            watcher.Path = di.FullName;
            watcher.Filter = "Notes.ln";
            watcher.Changed += new FileSystemEventHandler(watcher_Changed);
            watcher.EnableRaisingEvents = true;

            ContextMenuStrip notifyIconMenu = new ContextMenuStrip();
            ToolStripMenuItem menuItemOpen = new ToolStripMenuItem("Open", null, delegate { Activate(); });
            menuItemOpen.Font = new System.Drawing.Font(menuItemOpen.Font, System.Drawing.FontStyle.Bold);
            notifyIconMenu.Items.Add(menuItemOpen);
            notifyIconMenu.Items.Add("Exit", null, delegate { Shutdown(); });
            notifyIconMenu.ShowImageMargin = false;

            notifyIcon = new NotifyIcon();
            notifyIcon.Text = "Light Notes";
            notifyIcon.Icon = Properties.Resources.Notes;
            notifyIcon.ContextMenuStrip = notifyIconMenu;
            notifyIcon.MouseClick += delegate(object sender, System.Windows.Forms.MouseEventArgs e)
            {
                if (e.Button == MouseButtons.Left) { Activate(); }
                if (e.Button == MouseButtons.Right) notifyIcon.ContextMenuStrip.Show();
            };
            notifyIcon.MouseDoubleClick += delegate { Activate(); };
        }

        private void watcher_Changed(object sender, FileSystemEventArgs e)
        {
            watcher.EnableRaisingEvents = false;
            System.Threading.Thread.Sleep(500);
            watcher.EnableRaisingEvents = true;
            Dispatcher.Invoke((MethodInvoker)delegate { Load(); }, System.Windows.Threading.DispatcherPriority.ContextIdle);
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            Hide();
            notifyIcon.Visible = true;
            RegisterHotkey();
        }

        public void FlagChange()
        {
            isChanged = true;
        }

        public void RegisterHotkey()
        {
            if (hotkey != null) hotkey.Dispose();
            hotkey = new HotkeyEx(Properties.Settings.Default.hotkey);
            hotkey.HotkeyPressed += delegate { Activate(); };
            try
            {
                hotkey.Enabled = true;
            }
            catch (ManagedWinapi.HotkeyAlreadyInUseException)
            {
                notifyIcon.BalloonTipTitle = "Light Notes (Error)";
                notifyIcon.BalloonTipText = "Hotkey is already in use. Click here to change hotkey.";
                notifyIcon.BalloonTipClicked += delegate { Activate(); ShowOptions(); };
                notifyIcon.ShowBalloonTip(0);
            }
        }

        public void RegisterAutostart()
        {
            RegistryKey regKey = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Run\", true);
            if (Properties.Settings.Default.autostart)
                regKey.SetValue("Light Notes", System.Reflection.Assembly.GetExecutingAssembly().Location);
            else
                if (regKey.GetValue("Light Notes") != null) regKey.DeleteValue("Light Notes");
            regKey.Close();
        }

        private void HideWindow()
        {
            Hide();
            Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Background, (MethodInvoker)Save);
        }

        private void MainWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal, (MethodInvoker)HideWindow);
            e.Cancel = true;
        }

        private void Shutdown()
        {
            Save();
            hotkey.Dispose();
            notifyIcon.Dispose();
            watcher.Dispose();
            App.Current.Shutdown();
        }

        private void Window_Activated(object sender, EventArgs e)
        {
            if (!IsVisible) Show();
            if (WindowState == WindowState.Minimized) WindowState = Properties.Settings.Default.state;
        }

        private void Window_StateChanged(object sender, EventArgs e)
        {
            if (WindowState == WindowState.Minimized) HideWindow();
            else Properties.Settings.Default.state = WindowState;
        }

        private void Grid_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (e.ClickCount == 2 && e.OriginalSource.GetType() == typeof(Grid)) NewTab();
        }

        public void NewTab()
        {
            TabItem ti = new NotesTab();
            Tabs.Items.Add(ti);
            Tabs.SelectedItem = ti;
            FlagChange();
        }

        public void DeleteTab(NotesTab notesTab)
        {
            if (System.Windows.MessageBox.Show("Are you sure you want to delete \"" + notesTab.Header + "\"?", "Delete Tab",
                MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes)
            {
                Tabs.Items.Remove(notesTab);
                FlagChange();
            }
        }

        public void Reload()
        {
            DirectoryInfo di = new DirectoryInfo(Properties.Settings.Default.folder);
            di.Create();
            fi = new FileInfo(di.FullName + @"\Notes.ln");
            if (fi.Exists)
            {
                fi.CopyTo(fi.FullName + ".bak", true);
                Load();
            }
            else FlagChange();
            watcher.Path = di.FullName;
        }

        private void Save()
        {
            Properties.Settings.Default.left = Left;
            Properties.Settings.Default.top = Top;
            Properties.Settings.Default.width = Width;
            Properties.Settings.Default.height = Height;
            Properties.Settings.Default.Save();

            if (!isChanged) return;
            watcher.EnableRaisingEvents = false;

            using (Stream fStream = new FileStream(fi.FullName, FileMode.Create, FileAccess.Write, FileShare.None))
            {
                List<Header> headers = new List<Header>();
                List<MemoryStream> contents = new List<MemoryStream>();

                foreach (NotesTab nt in Tabs.Items)
                {
                    MemoryStream s = new MemoryStream();
                    FlowDocument doc = nt.NoteBox.Document;
                    TextRange range = new TextRange(doc.ContentStart, doc.ContentEnd);
                    range.Save(s, System.Windows.DataFormats.XamlPackage);
                    headers.Add(new Header(nt.Header as string, (int)s.Length));
                    contents.Add(s);
                }
                
                BinaryFormatter binFormat = new BinaryFormatter();
                binFormat.Serialize(fStream, headers);
                foreach (MemoryStream s in contents)
                {
                    s.WriteTo(fStream);
                    s.Close();
                }
            }

            isChanged = false;
            watcher.EnableRaisingEvents = true;
        }

        private void Load()
        {
            Tabs.Items.Clear();
            using (Stream fStream = new FileStream(fi.FullName, FileMode.Open, FileAccess.Read, FileShare.None))
            {
                List<Header> headers;
                BinaryFormatter binFormat = new BinaryFormatter();
                headers = binFormat.Deserialize(fStream) as List<Header>;

                foreach (Header h in headers)
                {
                    NotesTab ti = new NotesTab();
                    ti.Header = h.Name;
                    MemoryStream s = new MemoryStream();
                    for (int i = 0; i < h.Length; i++) s.WriteByte((byte)fStream.ReadByte());
                    FlowDocument doc = ti.NoteBox.Document;
                    TextRange range = new TextRange(doc.ContentStart, doc.ContentEnd);
                    range.Load(s, System.Windows.DataFormats.XamlPackage);
                    Tabs.Items.Add(ti);
                    s.Close();
                }
                Tabs.SelectedIndex = 0;
            }

            isChanged = false;
        }

        private void new_Click(object sender, RoutedEventArgs e)
        {
            NewTab();
        }

        private void settings_Click(object sender, RoutedEventArgs e)
        {
            ShowOptions();
        }

        private void ShowOptions()
        {
            Options optionsWindow = new Options();
            optionsWindow.Top = Top + 65;
            optionsWindow.Left = Left + Width - 385;
            optionsWindow.ShowDialog();
        }

        public void Find_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            (Tabs.SelectedItem as NotesTab).NoteBox.Focus();
            FindBar.ReplaceBar.Visibility = Visibility.Collapsed;
            FindBar.Visibility = Visibility.Visible;
            Dispatcher.Invoke((MethodInvoker)delegate { FindBar.FindText.Focus(); }, System.Windows.Threading.DispatcherPriority.ContextIdle);
        }

        private void Find_CanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = true;
        }

        private void Replace_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            (Tabs.SelectedItem as NotesTab).NoteBox.Focus();
            FindBar.ReplaceBar.Visibility = Visibility.Visible;
            FindBar.Visibility = Visibility.Visible;
            Dispatcher.Invoke((MethodInvoker)delegate { FindBar.FindText.Focus(); }, System.Windows.Threading.DispatcherPriority.ContextIdle);
        }

        private void Replace_CanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = true;
        }

        public void CloseFind()
        {
            FindBar.Visibility = Visibility.Collapsed;
            (Tabs.SelectedItem as NotesTab).NoteBox.Focus();
        }

        private void Window_KeyDown(object sender, System.Windows.Input.KeyEventArgs e)
        {
            if (e.Key == Key.Escape && FindBar.Visibility == Visibility.Visible) CloseFind();
        }
    }

    [Serializable]
    class Header
    {
        public string Name { get; set; }
        public int Length { get; set; }

        public Header(string name, int length)
        {
            Name = name;
            Length = length;
        }
    }
}
